﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Windows.Media.Media3D10
{
    /// <summary>
    /// Représente une rotation 3D d'un angle spécifié à propos d'un axe spécifié.
    /// </summary>
    public sealed class AxisAngleRotation3D10 : Rotation3D10
    {
        #region Dependency Properties
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="Angle"/>.
        /// </summary>
        public static readonly DependencyProperty AngleProperty =
            DependencyProperty.Register("Angle", typeof(float), typeof(AxisAngleRotation3D10),
            new PropertyMetadata(0.0f, new PropertyChangedCallback(Angle_PropertyChanged)));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="Axis"/>.
        /// </summary>
        public static readonly DependencyProperty AxisProperty =
            DependencyProperty.Register("Axis", typeof(SlimDX.Vector3), typeof(AxisAngleRotation3D10),
            new PropertyMetadata(new PropertyChangedCallback(Axis_PropertyChanged)));
        #endregion

        #region DependencyProperties Callbacks
        private static void Angle_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ((AxisAngleRotation3D10)sender).OnAngleChanged(e);
        }
        
        private static void Axis_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ((AxisAngleRotation3D10)sender).OnAxisChanged(e);
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Crée une instance d'une rotation 3D.
        /// </summary>
        public AxisAngleRotation3D10()
        {
            this.Axis = SlimDX.Vector3.Zero;
            ComputeValue();
        }

        /// <summary>
        /// Crée une instance d'une rotation 3D à l'aide de l'axe et de l'angle spécifiés.
        /// </summary>
        /// <param name="axis"><see cref="SlimDX.Vector3"/> qui spécifie l'axe autour duquel pivoter.</param>
        /// <param name="angle">Float qui spécifie l'angle de rotation, en degrés.</param>
        public AxisAngleRotation3D10(SlimDX.Vector3 axis, float angle)
        {
            this.Axis = axis;
            this.Angle = angle;
        }
        #endregion

        #region Data
        private SlimDX.Matrix _value;
        #endregion

        #region Public Properties
        /// <summary>
        /// Obtient ou définit l'angle d'une rotation 3D, en degrés. 
        /// </summary>
        public float Angle
        { 
            get { return (float)GetValue(AngleProperty); }
            set { SetValue(AngleProperty, value); }
        }

        /// <summary>
        /// Obtient ou définit l'axe d'une rotation 3D. 
        /// </summary>
        [System.ComponentModel.TypeConverter(typeof(Vector3TypeConverter))]
        public SlimDX.Vector3 Axis
        {
            get { return (SlimDX.Vector3)GetValue(AxisProperty); }
            set { SetValue(AxisProperty, value); }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Crée un clone modifiable de ce <see cref="AxisAngleRotation3D10"/>, en créant des copies complètes des valeurs de cet objet.
        /// Lors de la copie de propriétés de dépendance, cette méthode copie les références de ressources et les liaisons de données (même si elles ne peuvent plus être résolues), mais pas les animations ni leurs valeurs actuelles.
        /// </summary>
        /// <returns>
        /// Clone modifiable de l'objet actuel.
        /// La valeur de la propriété <see cref="Freezable.IsFrozen"/> de l'objet cloné est false, même si la valeur de la propriété <see cref="Freezable.IsFrozen"/> de la source est true.
        /// </returns>
        public new AxisAngleRotation3D10 Clone()
        {
            return base.Clone() as AxisAngleRotation3D10;
        }

        /// <summary>
        /// Crée un clone modifiable de ce <see cref="AxisAngleRotation3D10"/>, en créant des copies complètes des valeurs actuelles de cet objet.
        /// Contrairement à leurs valeurs actuelles, les références de ressources, les liaisons de données et les animations ne sont pas copiées.
        /// </summary>
        /// <returns>
        /// Clone modifiable de l'objet actuel.
        /// La valeur de la propriété <see cref="Freezable.IsFrozen"/> de l'objet cloné est false, même si la valeur de la propriété <see cref="Freezable.IsFrozen"/> de la source est true.
        /// </returns>
        public new AxisAngleRotation3D10 CloneCurrentValue()
        {
            return base.CloneCurrentValue() as AxisAngleRotation3D10;
        }
        #endregion

        #region Private Members
        private void OnAngleChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ComputeValue();
        }

        private void OnAxisChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            ComputeValue();
        }

        private void ComputeValue()
        {
            this._value = SlimDX.Matrix.RotationAxis(Axis, Angle.ToRadians());
        }
        #endregion

        #region Rotation3D10 Implementation
        /// <summary>
        /// Représentation matricielle de la transformation
        /// </summary>
        protected internal override SlimDX.Matrix Value { get { return this._value; } }
        #endregion

        #region Freezable Implementation
        /// <summary>
        /// Une fois implémentée dans une classe dérivée, initialise une nouvelle instance de la classe dérivée <see cref="Freezable"/>. 
        /// </summary>
        /// <returns>Nouvelle instance.</returns>
        protected override Freezable CreateInstanceCore()
        {
            return new AxisAngleRotation3D10();
        }
        #endregion
    }
}
